/*
 * $Id: JMapGrid.java 24 2009-03-31 23:40:10Z aaron.day $
 */
package org.crazydays.gameplan.map.swing;


import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;

import javax.swing.Scrollable;
import javax.swing.SwingConstants;

import org.crazydays.gameplan.map.GridType;
import org.crazydays.gameplan.map.IGameMap;

/**
 * JMapGeometry
 */
public class JMapGrid
        extends Component
        implements Scrollable
{
	/** serial version uid */
	public final static long serialVersionUID =
	        "$Id: JMapGrid.java 24 2009-03-31 23:40:10Z aaron.day $"
	                .hashCode();

	/** game map */
	protected IGameMap gameMap;

	/** max unit increment */
	protected int maxUnitIncrement = 1;

	/** lines */
	protected BufferedImage lines;

	/**
	 * JMapGeometry constructor.
	 * 
	 * @param gameMap Game map
	 */
	public JMapGrid(IGameMap gameMap)
	{
		setGameMap(gameMap);
		initialize();
	}

	/**
	 * Initialize map geometry.
	 */
	protected void initialize()
	{
		// initialize geometry
		setSize(getGameMap().getGeometry().getWidth(), getGameMap()
		        .getGeometry().getHeight());

		lines =
		        new BufferedImage(
		                getGameMap().getGeometry().getWidth(),
		                getGameMap().getGeometry().getHeight(),
		                BufferedImage.TYPE_4BYTE_ABGR);

		initializeLines();
	}

	/**
	 * Initialize map lines.
	 */
	protected void initializeLines()
	{
		GridType type = getGameMap().getGeometry().getType();

		if (type == GridType.SQUARE) {
			initializeGridLines();
		} else if (type == GridType.HEXAGON) {
			initializeHexagonLines();
		} else if (type == GridType.DIAMOND) {
			initializeIsometricLines();
		} else {
			throw new UnsupportedOperationException(
			        "Unknown GeometryType: " + type);
		}
	}

	/**
	 * Initialize grid lines.
	 */
	protected void initializeGridLines()
	{
		int size = getGameMap().getGeometry().getSize();

		Graphics graphics = lines.getGraphics();

		// vertical lines
		for (int i = 0; i < lines.getWidth(); i += size) {
			graphics.drawLine(i, 0, i, lines.getHeight());
		}

		// horizontal lines
		for (int i = 0; i < lines.getHeight(); i += size) {
			graphics.drawLine(0, i, lines.getWidth(), i);
		}
	}

	/**
	 * Initialize hexagon lines.
	 */
	protected void initializeHexagonLines()
	{
		int size = getGameMap().getGeometry().getSize();

		Graphics graphics = lines.getGraphics();

		for (int i = 0; i < lines.getWidth(); i += size * 3) {
			for (int j = 0; j < lines.getHeight(); j +=
			        (Math.cos(Math.PI * 1 / 6) * size) * 2) {

				// current row
				drawHexMapLine(graphics, i, j, size);

				// next row
				drawHexMapLine(graphics, i + (size * 3 / 2),
				        (int) (j + Math.cos(Math.PI * 1 / 6)
				                * size), size);
			}
		}
	}

	/**
	 * Draw a hexagon map.
	 * 
	 * @param graphics Graphics
	 * @param x X
	 * @param y Y
	 * @param size Size
	 */
	protected void drawHexMapLine(Graphics graphics, int x, int y, int size)
	{
		int[] xs = new int[7];
		int[] ys = new int[7];

		xs[0] = xs[6] = x + (int) (Math.sin(Math.PI * 1 / 6) * size);
		ys[0] = ys[6] = y + (int) (Math.cos(Math.PI * 1 / 6) * size);

		xs[1] = x + size;
		ys[1] = y;

		xs[2] = x + (int) (Math.sin(Math.PI * 1 / 6) * size);
		ys[2] = y - (int) (Math.cos(Math.PI * 1 / 6) * size);

		xs[3] = x - (int) (Math.sin(Math.PI * 1 / 6) * size);
		ys[3] = y - (int) (Math.cos(Math.PI * 1 / 6) * size);

		xs[4] = x - size;
		ys[4] = y;

		xs[5] = x - (int) (Math.sin(Math.PI * 1 / 6) * size);
		ys[5] = y + (int) (Math.cos(Math.PI * 1 / 6) * size);

		// for (int i = 0; i < 6; i++) {
		// System.out.println("point[" + i + "]: (" + xs[i] + ","
		// + ys[i] + ")");
		// }

		graphics.drawPolyline(xs, ys, 7);
	}

	/**
	 * Initialize isometric lines.
	 */
	protected void initializeIsometricLines()
	{
		int size = getGameMap().getGeometry().getSize();

		Graphics graphics = lines.getGraphics();

		int ydiff =
		        (int) (lines.getHeight() * Math.sin((Math.PI * 1 / 6)));

		// slash lines
		for (int i = 0; i < (lines.getHeight() + ydiff); i += size) {
			graphics.drawLine(0, i, lines.getWidth(), i - ydiff);
		}

		// backslash lines
		for (int i = roundDown(-ydiff, size); i < lines.getHeight(); i +=
		        size) {
			graphics.drawLine(0, i, lines.getWidth(), i + ydiff);
		}
	}

	/**
	 * Round down to the nearest.
	 * 
	 * @param value Value
	 * @param nearest Nearest
	 * @return Value
	 */
	private int roundDown(int value, int nearest)
	{
		int times = value / nearest;
		int left = value % nearest;

		return (times * nearest) - (left > 0 ? left : 0);
	}

	/**
	 * Set game map.
	 * 
	 * @param gameMap Game map
	 */
	public void setGameMap(IGameMap gameMap)
	{
		this.gameMap = gameMap;
	}

	/**
	 * Get game map.
	 * 
	 * @return Game map
	 */
	public IGameMap getGameMap()
	{
		return gameMap;
	}

	/**
	 * Get preferred size.
	 * 
	 * @return Size
	 * @see java.awt.Component#getPreferredSize()
	 */
	public Dimension getPreferredSize()
	{
		return new Dimension(getGameMap().getGeometry().getWidth(),
		        getGameMap().getGeometry().getHeight());
	}

	/**
	 * Paint image.
	 * 
	 * @param graphics Graphics
	 * @see java.awt.Component#paint(java.awt.Graphics)
	 */
	public void paint(Graphics graphics)
	{
		graphics.drawImage(lines, 0, 0, null);
	}

	/**
	 * Get preferred scrollable viewport size.
	 * 
	 * @return Dimension
	 * @see javax.swing.Scrollable#getPreferredScrollableViewportSize()
	 * @see Component#getPreferredSize()
	 */
	public Dimension getPreferredScrollableViewportSize()
	{
		return getPreferredSize();
	}

	/**
	 * Get scrollable tracks viewport height.
	 * 
	 * @return Tracks
	 * @see javax.swing.Scrollable#getScrollableTracksViewportHeight()
	 */
	public boolean getScrollableTracksViewportHeight()
	{
		return false;
	}

	/**
	 * Get scrollable tracks viewport width.
	 * 
	 * @return Tracks
	 * @see javax.swing.Scrollable#getScrollableTracksViewportWidth()
	 */
	public boolean getScrollableTracksViewportWidth()
	{
		return false;
	}

	/**
	 * Get scrollable unit increment.
	 * 
	 * @param visibleRect Visible rectangle
	 * @param orientation Orientation
	 * @param direction Direction
	 * @return Increment
	 * @see javax.swing.Scrollable#getScrollableUnitIncrement(Rectangle,int,int)
	 */
	public int getScrollableUnitIncrement(Rectangle visibleRect,
	        int orientation, int direction)
	{
		// Get the current position.
		int currentPosition = 0;
		if (orientation == SwingConstants.HORIZONTAL) {
			currentPosition = visibleRect.x;
		} else {
			currentPosition = visibleRect.y;
		}

		// Return the number of pixels between currentPosition
		// and the nearest tick mark in the indicated direction.
		if (direction < 0) {
			int newPosition =
			        currentPosition
			                - (currentPosition / maxUnitIncrement)
			                * maxUnitIncrement;
			return (newPosition == 0) ? maxUnitIncrement
			        : newPosition;
		} else {
			return ((currentPosition / maxUnitIncrement) + 1)
			        * maxUnitIncrement - currentPosition;
		}
	}

	/**
	 * Get scrollable block increment.
	 * 
	 * @param visibleRect Visisble rectangle
	 * @param orientation Orientation
	 * @param direction Direction
	 * @return Increment
	 * @see javax.swing.Scrollable#getScrollableBlockIncrement(Rectangle,int,int)
	 */
	public int getScrollableBlockIncrement(Rectangle visibleRect,
	        int orientation, int direction)
	{
		if (orientation == SwingConstants.HORIZONTAL) {
			return visibleRect.width - maxUnitIncrement;
		} else {
			return visibleRect.height - maxUnitIncrement;
		}
	}
}
